
/*
 * Copyright (C) 2008-2009 Archie L. Cobbs. All rights reserved.
 *
 * $Id: MapTest1.java 377 2010-03-11 20:50:18Z archie.cobbs $
 */

package org.dellroad.sidekar;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

import org.dellroad.sidekar.annotation.SidekarEntity;
import org.dellroad.sidekar.annotation.SidekarMap;
import org.dellroad.sidekar.util.VoidDatabaseAction;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertSame;

public class MapTest1 extends DatabaseTest {

    private Top top;
    private Key[] keys;
    private Val[] vals;

    @Test
    public void testMap1() {

        // Populate
        this.db.performTransaction(null, new VoidDatabaseAction() {
            public void executeVoid() {
                populateDB();
            }
        });

        // Verify
        this.db.performTransaction(null, new VoidDatabaseAction() {
            public void executeVoid() {
                verifyDB();
            }
        });

        // Export
        compareExport();

        // Verify
        this.db.performTransaction(null, new VoidDatabaseAction() {
            public void executeVoid() {
                checkDetach();
            }
        });
    }

    protected void populateDB() {

        // Create instances
        this.top = this.db.create(Top.class);
        this.keys = new Key[] {
            this.db.create(Key.class),
            this.db.create(Key.class),
            this.db.create(Key.class),
            this.db.create(Key.class),
        };
        this.vals = new Val[] {
            this.db.create(Val.class),
            this.db.create(Val.class),
            this.db.create(Val.class),
            this.db.create(Val.class),
        };

        // Populate maps
        Map<Key, Val> objMap = this.top.getObjMap();
        objMap.put(this.keys[0], this.vals[2]);
        objMap.put(this.keys[0], this.vals[0]);
        objMap.put(this.keys[1], this.vals[1]);
        objMap.remove(this.keys[3]);
        objMap.put(this.keys[2], this.vals[1]);
        objMap.put(this.keys[3], this.vals[1]);
        objMap.remove(this.keys[3]);
        objMap.put(null, null);

        Map<Integer, Integer> intMap = this.top.getIntMap();
        intMap.put(123, 456);
        intMap.put(123, 789);
        intMap.put(456, 321);
        intMap.put(321, 555);
        intMap.put(Integer.MIN_VALUE, Integer.MAX_VALUE);
        intMap.remove(321);
    }

    protected void verifyDB() {

        // Check attachedness
        assert this.top.isSidekarAttached();
        assert this.keys[0].isSidekarAttached();
        assert this.keys[1].isSidekarAttached();
        assert this.keys[2].isSidekarAttached();
        assert this.keys[3].isSidekarAttached();
        assert this.vals[0].isSidekarAttached();
        assert this.vals[1].isSidekarAttached();
        assert this.vals[2].isSidekarAttached();
        assert this.vals[3].isSidekarAttached();

        // Check maps
        Map<Key, Val> objMap = this.top.getObjMap();
        assertEquals(objMap.size(), 4);
        assertSame(objMap.get(this.keys[0]), this.vals[0]);
        assertSame(objMap.get(this.keys[1]), this.vals[1]);
        assertSame(objMap.get(this.keys[2]), this.vals[1]);
        assertNull(objMap.get(this.keys[3]));
        assertNull(objMap.get(null));
        assertEquals((Object)objMap.keySet(), (Object)new InlineSet(this.keys[0], this.keys[1], this.keys[2], null));
        assertEquals((Object)new HashSet<Val>(objMap.values()), (Object)new InlineSet(this.vals[0], this.vals[1], null));

        Map<Integer, Integer> intMap = this.top.getIntMap();
        assertEquals(intMap.size(), 3);
        assertEquals((Object)intMap.get(123), 789);
        assertEquals((Object)intMap.get(456), 321);
        assertNull(intMap.get(321));
        assertEquals((Object)intMap.get(Integer.MIN_VALUE), Integer.MAX_VALUE);
        assertEquals((Object)intMap.keySet(), (Object)new InlineSet(123, 456, Integer.MIN_VALUE));
        assertEquals((Object)new HashSet<Integer>(intMap.values()), (Object)new InlineSet(789, 321, Integer.MAX_VALUE));
    }

    protected void checkDetach() {

        // Detach top
        this.top.sidekarDetach();
        try {
            this.top.getObjMap();
            assert false;
        } catch (DetachedItemException e) {
            // expected
        }

        // Check attachedness
        assert this.keys[0].isSidekarAttached();
        assert this.keys[1].isSidekarAttached();
        assert this.keys[2].isSidekarAttached();
        assert this.keys[3].isSidekarAttached();
        assert this.vals[0].isSidekarAttached();
        assert this.vals[1].isSidekarAttached();
        assert this.vals[2].isSidekarAttached();
        assert this.vals[3].isSidekarAttached();
    }

    @Override
    protected List<Class<?>> getSchemaClasses() {
        return Arrays.<Class<?>>asList(Top.class, Key.class, Val.class);
    }

    public static class InlineSet extends HashSet<Object> {
        public InlineSet(Object... elems) {
            for (Object r : elems)
                add(r);
        }
    }

    // Model classes

    @SidekarEntity
    public abstract static class Top implements Item {

        @SidekarMap
        public abstract Map<Key, Val> getObjMap();

        @SidekarMap
        public abstract Map<Integer, Integer> getIntMap();
    }

    @SidekarEntity
    public abstract static class Key extends Top implements Item {
    }

    @SidekarEntity
    public abstract static class Val extends Top implements Item {
    }
}

